package zdream.rockchronicle.screen.menu;

import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.IntMap;
import com.badlogic.gdx.utils.IntMap.Entry;

import zdream.control.input.WorldInput;
import zdream.rockchronicle.RockChronicle;
import zdream.control.input.IMapKeyListener;
import zdream.rockchronicle.core.input.IKeyListener;

import static zdream.rockchronicle.core.input.InputCenter.*;

import java.util.Objects;
import java.util.function.Consumer;

/**
 * 菜单界面
 * 
 * @author Zdream
 * @date 2021-08-22
 */
public class GameMenu {
	public final RockChronicle app;
	final GameMenuPainter painter;

	public GameMenu(RockChronicle app) {
		this.app = Objects.requireNonNull(app);
		this.painter = new GameMenuPainter(this);
	}

	public void init() {
		OrthographicCamera symbolCamera = new OrthographicCamera();
		symbolCamera.setToOrtho(false, app.width, app.height);

		this.painter.symbolCamera = symbolCamera;
		this.painter.init();
		app.graphics.addUIPainter(this.painter);

		app.input.p1.addControlListener(openMenu, WorldInput.MAP_START);
	}

	/* **********
	 *   状态   *
	 ********** */
	public static final long CLOSE_DURATION = 400;

	public static final byte STATUS_CLOSE = 0;
	public static final byte STATUS_OPEN = 2;
	public static final byte STATUS_CLOSING = 3;
	public byte status = STATUS_CLOSE;

	public int opDuration = 0;

	public boolean open() {
		if (status != STATUS_CLOSE) {
			return false;
		}
		status = STATUS_OPEN;
		app.input.p1.addControlListener(inMenu,
				MAP_ATTACK, MAP_JUMP, MAP_SPECIAL, MAP_STRONG,
				MAP_UP, MAP_DOWN, MAP_LEFT, MAP_RIGHT, MAP_START, MAP_BACK);
		toMain();
		app.levels.pause();
		
		return true;
	}
	
	public boolean close() {
		if (status != STATUS_OPEN) {
			return false;
		}
		items.clear();
		status = STATUS_CLOSING;
		app.input.p1.removeControlListener(inMenu);
		opDuration = 0;
		return true;
	}
	
	/* **********
	 *   绘制   *
	 ********** */

	public void tick(float delta) {
		long lDuration = (long) (delta * 1000);

		if (status == STATUS_CLOSING) {
			opDuration += lDuration;
			if (opDuration >= CLOSE_DURATION) {
				status = STATUS_CLOSE;
				app.levels.resume();
				opDuration = 0;
			}
		}
	}
	
	/* **********
	 *   位置   *
	 ********** */
	/**
	 * 当前位置, 0 表示主位置, 1 表示键位设置
	 */
	public int getPosition() {
		return items.size > 0 ? items.items[0].position : -1;
	}
	public static final int POSITION_MAIN = 0;
	public static final int POSITION_KEY_SETTING = 1;
	
	Array<MenuItem> items = new Array<>(MenuItem.class);
	/**
	 * 每一个菜单的层级叠起来像栈一样
	 */
	static class MenuItem {
		final Array<MenuItem> items;
		int position;
		int cursor;
		int cursorMax;
		int cursorHorizontal;
		
		public MenuItem(Array<MenuItem> items) {
			this.items = items;
		}
		void left() {}
		void right() {}
		void up() {
			cursor--;
			if (cursor < 0) cursor = 0;
		}
		void down() {
			cursor++;
			if (cursor > cursorMax) cursor = cursorMax;
		}
		void enter() {}
		void back() {
			destroy();
		}
		void destroy() {
			items.removeIndex(0);
		}
	}

	static class MainMenuItem extends MenuItem {
		public static final String TEXT_BACK_TO_GAME = "回到游戏";
		public static final String TEXT_KEY_SETTING = "按键设置";
		public static final String TEXT_EXIT_GAME = "退出游戏";
		public static final String[] TEXTS = new String[] {TEXT_BACK_TO_GAME, TEXT_KEY_SETTING, TEXT_EXIT_GAME};
		
		final RockChronicle app;
		public MainMenuItem(RockChronicle app, Array<MenuItem> items) {
			super(items);
			this.app = app;
			position = POSITION_MAIN;
			cursorMax = TEXTS.length - 1;
		}
		@Override
		void enter() {
			switch (TEXTS[cursor]) {
			case TEXT_BACK_TO_GAME:
				back();
				break;
			case TEXT_EXIT_GAME:
				app.exit();
				break;
			case TEXT_KEY_SETTING:
				MenuItem item = new KeySettingItem(app, items);
				items.insert(0, item);
				break;
			default:
				break;
			}
		}
		@Override
		void destroy() {
			super.destroy();
			exitListener.accept(this);
		}
		Consumer<MainMenuItem> exitListener;
	}

	static class KeySettingItem extends MenuItem implements IKeyListener {
		public static final String TEXT_UP = "上";
		public static final String TEXT_DOWN = "下";
		public static final String TEXT_LEFT = "左";
		public static final String TEXT_RIGHT = "右";
		public static final String TEXT_ATTACK = "攻击";
		public static final String TEXT_JUMP = "跳跃";
		public static final String TEXT_SPECIAL = "特殊武器攻击";
		public static final String TEXT_STRONG = "连发 | 重攻击";
		public static final String TEXT_SWITCH_1 = "切换武器一";
		public static final String TEXT_SWITCH_2 = "切换武器二";
		public static final String TEXT_SWITCH_3 = "切换武器三";
		public static final String TEXT_SWITCH_4 = "切换武器四";
		public static final String TEXT_SWITCH_5 = "切换武器五";
		public static final String TEXT_SWITCH_6 = "切换武器六";
		public static final String TEXT_SWITCH_7 = "切换武器七";
		public static final String TEXT_SWITCH_8 = "切换武器八";
		public static final String TEXT_ABILITY_1 = "能力一";
		public static final String TEXT_ABILITY_2 = "能力二 | 滑铲";
		public static final String TEXT_PROP_1 = "道具一";
		public static final String TEXT_PROP_2 = "道具二";
		public static final String TEXT_PROP_3 = "道具三";
		public static final String TEXT_DETAIL = "显示辅助参数";
		public static final String TEXT_PLUS_1 = "辅助一 <当前无效>";
		public static final String TEXT_PLUS_2 = "辅助二 <当前无效>";
		public static final String TEXT_START = "游戏菜单";
		public static final String TEXT_BACK = "状态菜单";
		
		public static final String TEXT_RESET = "重置以上按键 <当前无效>";
		public static final String TEXT_SAVE = "保存 <当前无效>";
		public static final String TEXT_GO_BACK = "保存并退出";
		
		public static final String[] TEXTS = new String[] {
				TEXT_UP, TEXT_DOWN, TEXT_LEFT, TEXT_RIGHT, TEXT_ATTACK, TEXT_JUMP,
				TEXT_SPECIAL, TEXT_STRONG,
				TEXT_SWITCH_1, TEXT_SWITCH_2, TEXT_SWITCH_3, TEXT_SWITCH_4,
				TEXT_SWITCH_5, TEXT_SWITCH_6, TEXT_SWITCH_7, TEXT_SWITCH_8,
				TEXT_ABILITY_1, TEXT_ABILITY_2,
				TEXT_PROP_1, TEXT_PROP_2, TEXT_PROP_3, TEXT_DETAIL,
				TEXT_PLUS_1, TEXT_PLUS_2, TEXT_START, TEXT_BACK,
				TEXT_RESET, TEXT_SAVE, TEXT_GO_BACK
				};
		
		static final IntMap<String> KEY_TEXT_MAP = new IntMap<>();
		
		static {
			KEY_TEXT_MAP.put(MAP_UP, TEXT_UP);
			KEY_TEXT_MAP.put(MAP_DOWN, TEXT_DOWN);
			KEY_TEXT_MAP.put(MAP_LEFT, TEXT_LEFT);
			KEY_TEXT_MAP.put(MAP_RIGHT, TEXT_RIGHT);
			KEY_TEXT_MAP.put(MAP_ATTACK, TEXT_ATTACK);
			KEY_TEXT_MAP.put(MAP_JUMP, TEXT_JUMP);
			KEY_TEXT_MAP.put(MAP_SPECIAL, TEXT_SPECIAL);
			KEY_TEXT_MAP.put(MAP_STRONG, TEXT_STRONG);
			
			KEY_TEXT_MAP.put(MAP_WEAPON1, TEXT_SWITCH_1);
			KEY_TEXT_MAP.put(MAP_WEAPON2, TEXT_SWITCH_2);
			KEY_TEXT_MAP.put(MAP_WEAPON3, TEXT_SWITCH_3);
			KEY_TEXT_MAP.put(MAP_WEAPON4, TEXT_SWITCH_4);
			KEY_TEXT_MAP.put(MAP_WEAPON5, TEXT_SWITCH_5);
			KEY_TEXT_MAP.put(MAP_WEAPON6, TEXT_SWITCH_6);
			KEY_TEXT_MAP.put(MAP_WEAPON7, TEXT_SWITCH_7);
			KEY_TEXT_MAP.put(MAP_WEAPON8, TEXT_SWITCH_8);

			KEY_TEXT_MAP.put(MAP_LB, TEXT_ABILITY_1);
			KEY_TEXT_MAP.put(MAP_RUSH, TEXT_ABILITY_2);
			KEY_TEXT_MAP.put(MAP_PROP1, TEXT_PROP_1);
			KEY_TEXT_MAP.put(MAP_PROP2, TEXT_PROP_2);
			KEY_TEXT_MAP.put(MAP_PROP3, TEXT_PROP_3);
			KEY_TEXT_MAP.put(MAP_DETAIL, TEXT_DETAIL);
			KEY_TEXT_MAP.put(MAP_LT, TEXT_PLUS_1);
			KEY_TEXT_MAP.put(MAP_RT, TEXT_PLUS_2);

			KEY_TEXT_MAP.put(MAP_START, TEXT_START);
			KEY_TEXT_MAP.put(MAP_BACK, TEXT_BACK);
		}
		
		final RockChronicle app;
		public KeySettingItem(RockChronicle app, Array<MenuItem> items) {
			super(items);
			this.app = app;
			position = POSITION_KEY_SETTING;
			cursorMax = TEXTS.length - 1;
			cursor = 0;
			cursorHorizontal = 1;
			app.input.keyListeners.add(this);
		}
		
		@Override
		void left() {
			if (mapKeyChange != -1) {
				return;
			}
			
			switch (TEXTS[cursor]) {
			case TEXT_RESET: case TEXT_SAVE: case TEXT_GO_BACK:
				cursorHorizontal = 0;
				break;
			default:
				cursorHorizontal = Math.max(1, cursorHorizontal - 1);
				break;
			}
		}
		
		@Override
		void right() {
			if (mapKeyChange != -1) {
				return;
			}
			
			switch (TEXTS[cursor]) {
			case TEXT_RESET: case TEXT_SAVE: case TEXT_GO_BACK:
				cursorHorizontal = 0;
				break;
			default:
				cursorHorizontal = Math.min(3, cursorHorizontal + 1);
				break;
			}
		}
		
		@Override
		void up() {
			if (mapKeyChange != -1) {
				return;
			}
			
			super.up();
			switch (TEXTS[cursor]) {
			case TEXT_RESET: case TEXT_SAVE: case TEXT_GO_BACK:
				cursorHorizontal = 0;
				break;
			default:
				if (cursorHorizontal == 0) cursorHorizontal = 1;
				break;
			}
		}
		
		@Override
		void down() {
			if (mapKeyChange != -1) {
				return;
			}
			
			super.down();
			switch (TEXTS[cursor]) {
			case TEXT_RESET: case TEXT_SAVE: case TEXT_GO_BACK:
				cursorHorizontal = 0;
				break;
			default:
				if (cursorHorizontal == 0) cursorHorizontal = 1;
				break;
			}
		}
		
		@Override
		void enter() {
			if (mapKeyChange != -1) {
				return;
			}
			
			switch (TEXTS[cursor]) {
			case TEXT_GO_BACK:
				back();
				break;
			case TEXT_RESET: case TEXT_SAVE:
				break;
			default:
				mapKeyChange = cursorTextToMapKey(TEXTS[cursor]);
				startTime = System.currentTimeMillis();
				break;
			}
		}
		
		@Override
		void back() {
			if (mapKeyChange != -1) {
				return;
			}
			
			super.back();
		}
		
		String[] queryBindingKeys(String text) {
			int mapKey = cursorTextToMapKey(text);
			if (mapKey == -1) {
				return null;
			}
			
			int[] keys = app.input.p1.queryKeyMap(mapKey);
			String[] keyTexts = new String[keys.length];
			for (int i = 0; i < keys.length; i++) {
				if (mapKeyChange != -1 && TEXTS[cursor].equals(text) && i == cursorHorizontal - 1) {
					keyTexts[i] = "<请输入>";
					continue;
				}
				
				switch (keys[i]) {
				case 0: keyTexts[i] = "<未设置>"; break;
				case Keys.UP: keyTexts[i] = "上"; break;
				case Keys.DOWN: keyTexts[i] = "下"; break;
				case Keys.LEFT: keyTexts[i] = "左"; break;
				case Keys.RIGHT: keyTexts[i] = "右"; break;
				case Keys.BACKSPACE: keyTexts[i] = "退格键"; break;
				case Keys.SPACE: keyTexts[i] = "空格键"; break;
				case Keys.ESCAPE: keyTexts[i] = "ESC"; break;
				case Keys.NUMPAD_0: keyTexts[i] = "小键盘0"; break;
				case Keys.NUMPAD_1: keyTexts[i] = "小键盘1"; break;
				case Keys.NUMPAD_2: keyTexts[i] = "小键盘2"; break;
				case Keys.NUMPAD_3: keyTexts[i] = "小键盘3"; break;
				case Keys.NUMPAD_4: keyTexts[i] = "小键盘4"; break;
				case Keys.NUMPAD_5: keyTexts[i] = "小键盘5"; break;
				case Keys.NUMPAD_6: keyTexts[i] = "小键盘6"; break;
				case Keys.NUMPAD_7: keyTexts[i] = "小键盘7"; break;
				case Keys.NUMPAD_8: keyTexts[i] = "小键盘8"; break;
				case Keys.NUMPAD_9: keyTexts[i] = "小键盘9"; break;
				case Keys.I: keyTexts[i] = "Ｉ"; break; // 这个是游戏 BUG
				default: keyTexts[i] = Keys.toString(keys[i]); break;
				}
			}
			return keyTexts;
		}

		int cursorTextToMapKey(String text) {
			for (Entry<String> entry : KEY_TEXT_MAP) {
				if (entry.value.equals(text)) {
					return entry.key;
				}
			}
			return -1;
		}
		
		long startTime = -1;
		int mapKeyChange = -1;
		
		@Override
		public void onKeyPressed(int keyCode) {
			if (mapKeyChange > 0 && keyCode != Keys.ESCAPE && System.currentTimeMillis() - startTime > 100) {
				app.input.p1.putKeyMap(mapKeyChange, keyCode, this.cursorHorizontal - 1);
				mapKeyChange = -1;
			}
		}

		@Override
		public void onKeyReleased(int keyCode) {}
		
		@Override
		void destroy() {
			super.destroy();
			app.input.keyListeners.removeValue(this, true);
		}
	}
	
	private void toMain() {
		MainMenuItem item = new MainMenuItem(app, items);
		item.exitListener = (i) -> close();
		items.insert(0, item);
	}

	IMapKeyListener inMenu = this::operateMenu;
	IMapKeyListener openMenu = this::pressEscToOpenMenu;

	private void operateMenu(int mapKey, WorldInput in) {
		switch (mapKey) {
		case MAP_UP:
			items.items[0].up();
			break;
		case MAP_DOWN:
			items.items[0].down();
			break;
		case MAP_LEFT:
			items.items[0].left();
			break;
		case MAP_RIGHT:
			items.items[0].right();
			break;
		case MAP_JUMP: case MAP_SPECIAL:
			items.items[0].enter();
			break;
		case MAP_ATTACK: case MAP_STRONG: case MAP_START: case MAP_BACK:
			items.items[0].back();
			if (items.size == 0) {
				close();
			}
			break;
		default:
			break;
		}
	}

	private void pressEscToOpenMenu(int mapKey, WorldInput in) {
		if (mapKey != MAP_START || status != STATUS_CLOSE) {
			return;
		}
		this.open();
	}
}
